/*
Copyright (c) 2010, Advanced Micro Devices
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this list of conditions 
and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this list of 
conditions and the following disclaimer in the documentation and/or other materials provided 
with the distribution.

Neither the name of Advanced Micro Devices nor the names of its contributors may be used to endorse 
or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

/*! \file
*   \brief PipelineKit top level declarations    
*   \author Alan Heirich
*   
*   \version 0.1
*   \date March 2010
*/

#ifndef __PIPELINE_KIT_HPP__
#define __PIPELINE_KIT_HPP__

#include "platform.h"

#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <fstream>
#include <string.h>

#include "ast.hpp"
#include "parser.h"

using namespace std;

namespace amd{

#define APPLICATION_NAME "pipeline-kit"
#define APPLICATION_VERSION "0.1"

	class Equation;
	class Code;
	class Pipeline;
	class Attribute;
	class Buffer;
	class Image2D;
	class Image3D;
	class Uniform;
	class Kernel;
	class Stage;
	class GraphVertex;
	class Graph;
	class Device;
	class Node;

	struct compareStrings 
	{
		bool operator()( std::string a, std::string b ) const
		{
			return a.compare( b ) < 0;
		}
	};

	typedef struct{
		Node * node;
		ADDRESS_SPACE addressSpace;
		int tokenType;
		std::string type;
		std::string ioClass;
		std::string name;
	} KernelParameter;

	typedef std::list<std::string> NameList;
	typedef std::list<KernelParameter> KernelParameterList;
	typedef std::list<Node *> NodeList;
	typedef map<std::string, Attribute *, compareStrings> AttributeMap;
	typedef map<std::string, GraphVertex *, compareStrings> GraphVertexMap;
	typedef std::vector<GraphVertex *> GraphVertexChildren;
	typedef map<std::string, Equation *, compareStrings> EquationMap;
	typedef multimap<std::string, Code *, compareStrings> CodeMap;
	typedef map<std::string, Buffer *, compareStrings> BufferMap;
	typedef vector<Buffer *> BufferVector;
	typedef map<std::string, Image2D *, compareStrings> Image2DMap;
	typedef map<std::string, Image3D *, compareStrings> Image3DMap;
	typedef map<std::string, Uniform *, compareStrings> UniformMap;
	typedef vector<Uniform *> UniformVector;
	typedef map<std::string, Kernel *, compareStrings> KernelMap;
	typedef map<std::string, Stage *, compareStrings> StageMap;
	typedef map<std::string, Graph *, compareStrings> GraphMap;
	typedef map<std::string, GraphVertex *, compareStrings> GraphVertexMap;
	typedef map<std::string, Pipeline *, compareStrings> PipelineMap;

	// class Node

	class Node{
	public:
		Node(){}
		virtual std::string class_(){ return ""; }
		void ptrIs( Node * ptr ){ ptr_ = ptr; }
		Node * ptr(){ return ptr_; }
	private:
		Node * ptr_;
	};

	// class Attribute

	class Attribute{
	public:
		Attribute(){}
		Attribute(
			std::string name,
			ASTExpr value )
		{
			name_ = name;
			value_ = value;
		}
		std::string
			name()
		{
			return name_;
		}
		ASTExpr
			value()
		{
			return value_;
		}
		bool
			isInstantiable()
		{
			bool result = true;
			if( 0 == name().compare( "target" )){
				result = false;
			}
			return result;
		}

		void
			selfDeclare( ofstream * classFile );
	private:
		std::string name_;
		ASTExpr value_;
	};


	// Clase AttributeContainer

	class AttributeContainerBase{
	public:
		AttributeContainerBase(){}
		AttributeMap &
			attributes()
		{
			return attributes_;
		}
		Pipeline * 
			pipeline()
		{ 
			return pipeline_;
		}
		void
			pipelineIs( Pipeline * pipeline )
		{
			pipeline_ = pipeline;
		}
		virtual Maybe<ASTExpr>::MaybeType maybeExpr( std::string name ) = 0;
		virtual ASTVarDefMap environment( void ) = 0;
		virtual bool isDefined( std::string name ) = 0;
		bool 
			isTrue( std::string name )
		{
			Maybe<ASTExpr>::MaybeType ma = maybeExpr( name );
			if( Maybe<ASTExpr>::toBool( ma )){
				ASTExpr a = Maybe<ASTExpr>::toJust( ma );
				return( evaluate<int>( environment(), a ) != 0 );
			}
			return false;
		}
		ASTExpr 
			expression( std::string name )
		{
			if( !isDefined( name ))
			{
				assert( 0 );
			}
			Maybe<ASTExpr>::MaybeType ma = maybeExpr( name );
			ASTExpr a = Maybe<ASTExpr>::toJust( ma );
			return a;
		}
		bool 
			isFalse( std::string name )
		{
			return !isTrue( name );
		}
		void 
			importAttributes( ASTAttributeList attributeList )
		{
			ASTAttributeList::iterator alIt;
			alIt = attributeList.begin();
			while( alIt != attributeList.end() ){
				ASTAttribute a = *alIt;
				std::string context = boost::get<0>( a );
				std::string name = boost::get<1>( a );
				ASTExpr e = boost::get< 2 >( a );
				std::string attributeName;
				if( !context.compare( "" ) ){
					attributeName = name;
				}
				else{
					attributeName = context + string( "." ) + name;
				}
				Attribute * A = new Attribute( attributeName, e );
				attributes()[ attributeName ] = A;
				alIt++;
			}//while
		}

	private:
		AttributeMap attributes_;
		Pipeline * pipeline_;
	};


	// The Pipeline class is the top level container for the pipeline.

	class Pipeline : public AttributeContainerBase{
	private:
		Pipeline(){}
	public:

		static Pipeline * createPipeline(
			std::string name, 
			ASTVarDefMap & environment, 
			ASTAttributeList & attributes )
		{
			Pipeline * pline  = new Pipeline();

			pline->name_ = name;
			pline->environment_ = environment;
			pline->setAttributeDefaults();
			pline->importAttributes( attributes );
			pline->importEquations( environment );

			return pline;
		}

		virtual ~Pipeline(){}
		void 
			setAttributeDefaults()
		{
#if 0
			Attribute * rangeAttribute = new Attribute( string( "range" ), ASTExpr( 0 ));
			attributes()[ "range" ] = rangeAttribute;
			Attribute * workgroupSizeAttribute = new Attribute( string( "workgroupSize" ), ASTExpr( 256 ));
			attributes()[ "workgroupSize" ] = workgroupSizeAttribute;
#endif
		}
		Equation *
			declareEquation( 
			std::string name,
			std::string type,
			ASTExpr & value );
		Code * 
			declareCode( 
			CODE_SECTION codeType, 
			std::string text,
			ASTAttributeList & attributes );
		Buffer * 
			declareBuffer( std::string name, 
			ADDRESS_SPACE addressSpace, 
			std::string type, 
			ASTExpr & size, 
			ASTAttributeList & attributes );
		Image2D * 
			declareImage2D( std::string name,
			ADDRESS_SPACE addressSpace, 
			std::string type, 
			ASTExpr & sizeX, 
			ASTExpr & sizeY,
			ASTAttributeList & attributes );	
		Image3D * 
			declareImage3D( std::string name,
			ADDRESS_SPACE addressSpace, 
			std::string type, 
			ASTExpr & sizeX, 
			ASTExpr & sizeY, 
			ASTExpr & sizeZ, 
			ASTAttributeList & attributes );	
		Uniform * 
			declareUniform( std::string name, 
			std::string type,
			ASTAttributeList & attributes );	
		Kernel *
			declareKernel( std::string name,
#if 1
			ASTTypeVarList & arguments,
#else
			ASTTypeVarList & inputs, 
			ASTTypeVarList & outputs, 
#endif
			ASTAttributeList & attributes );
		Stage * 
			declareStage( std::string name, 
			std::string kernel,
#if 1
			NameList & args,
#else
			NameList & inputs, 
			NameList & outputs, 
#endif
			ASTAttributeList & attributes );
		Graph * 
			declareGraph( std::string name, 
			ASTGraphEdgeList & edges, 
			ASTAttributeList & attributes );

		std::string
			name()
		{
			return name_;
		}
		EquationMap &
			equations()
		{
			return equations_;
		}
		CodeMap &
			codes()
		{
			return codes_;
		}
		BufferMap &
			buffers()
		{
			return buffers_;
		}
		Image2DMap &
			image2Ds()
		{
			return image2Ds_;
		}
		Image3DMap &
			image3Ds()
		{
			return image3Ds_;
		}
		UniformMap &
			uniforms()
		{
			return uniforms_;
		}
		KernelMap &
			kernels()
		{
			return kernels_;
		}
		StageMap &
			stages()
		{
			return stages_;
		}
		GraphMap &
			graphs()
		{
			return graphs_;
		}
		Maybe<ASTExpr>::MaybeType 
			maybeExpr( std::string name )
		{
			AttributeMap::iterator aIt = attributes().find( name );
			if( aIt != attributes().end() ){
				Attribute * a = aIt->second;
				Maybe<ASTExpr>::MaybeType aa = Maybe<ASTExpr>::MaybeType( a->value() );
				return aa;
			}
			return Maybe<ASTExpr>::MaybeType( boost::tuple<string>( "undefined" ) );
		}
		bool 
			isDefined( std::string name )
		{
			if( attributes().find( name ) != attributes().end() ){
				return true;
			}
			return false;
		}
		ASTVarDefMap
			environment()
		{
			return environment_;
		}
		void
			generateRuntimePipeline( void );
		void
			importEquations( ASTVarDefMap environment );
		bool
			targetIsOpenCL();
		bool
			targetIsDirectCompute();
		void
			implementQueueFinish( ofstream * file );
		void
			implementQueueRestart( ofstream * file );
		void
			checkName( std::string name );
		void
			checkExpression( std::string name, ASTExpr expression );
#if 0
		void
			findEquationTypes();
#endif

	private:
		std::string boilerplate( void );
		ofstream *
			outputFile( const char * baseName, const char * extension )
		{
			char fileName[ 1024 ];
			assert( strlen( baseName ) + strlen( extension ) + 1 < sizeof( fileName ));
			strcpy( fileName, baseName );
			strcat( fileName, extension );
			ofstream * result = new ofstream;
			result->open( (const char *)fileName, ios::out );
			return result;
		}
		ofstream *
			outputFile( const char * extension )
		{
			return outputFile( name().c_str(), extension );
		}
		void
			generateCLCodes( ofstream * kernelFile );
		void
			generateCPPCodes( ofstream * classFile, bool isTopLevel );
		void
			getInteropUsage( bool & glInterop, bool & dxInterop	);
		void
			generateInteropBools( ofstream * classFile );
		void
			generateBufferDeclarations( ofstream * classFile );
		void
			generateExternalStageDeclarations( ofstream * classFile );
		void
			generateSynchronizationStageDeclarations( ofstream * classFile );
		void
			generateClearSynchronization( ofstream * classFile );
		void
			generateStartSynchronization( ofstream * classFile, Graph * G );
		void
			generateExternalStageInvocations( ofstream * classFile );
		void
			generateUniformBuffers( ofstream * classFile );
		void
			generateKernelDeclarations( ofstream * classFile );
		void
			generateUniformDeclarations( ofstream * classFile );
		void
			generateAccessorMutators( ofstream * classFile );
		void
			generateAttributeDeclarations( ofstream * classFile );
		void
			generateTestDeclarations( ofstream * classFile );
		void 
			generateInitializeBuffersFunction( ofstream * classFile );
		void
			generateDirectComputeBufferDeclarations( ofstream * classFile );
		void
			generateInitializeStagesFunction( ofstream * classFile );
		void 
			generateInitializeKernelsFunction( ofstream * classFile );
		void
			generateInitializeAttributesFunction( ofstream * classFile );
		void
			generateSetArgsFunction( ofstream * classFile );
		void
			generateEquationEvaluationFunctions( ofstream * classFile );
		void
			generateInitializeEquationsFunction( ofstream * classFile );
		void 
			generateInitializeFunction( ofstream * classFile );
		void 
			generateTerminateFunction( ofstream * classFile );
		void
			invokeAcquireInterop( ofstream * classFile );
		void
			invokeReleaseInterop( ofstream * classFile );
		void
			generateAcquireInterop( ofstream * classFile );
		void
			generateReleaseInterop( ofstream * classFile );
		bool
			hasBufferAttribute( std::string attribute );
		bool 
			hasGlInteropBuffer();
		bool
			hasDxInteropBuffer();
		void
			generateStageInvocations( ofstream * classFile );
		void
			generateDerivedGraphFunctions( ofstream * classFile );
		void
			generateGraphBasicBlocks( ofstream * classFile );
		void
			generateExecutionFunctions(	std::string prefix, ofstream * classFile );
		void
			generateExecutionFunctions( ofstream * classFile );
		void
			declareComputeShaderSourceStrings( ofstream * classFile );
		void
			declareDirectComputeBuffers( Kernel * k, ofstream * kernelFile );
		void
			declareConstantBuffers( Kernel * k, ofstream * kernelFile );
		void
			generateComputeShaderHeaders();
		void 
			checkEquations();
		void 
			checkUniforms();
		void 
			checkBuffers();
		void 
			checkKernels();
		void 
			checkStages();
		void 
			checkGraphs();
		void
			checkStaticSemantics();
		void
			generateKernelHeaders();
		void
			generatePipelineClass();
		void
			generatePipelineMain();
		void
			generatePipelineExternals();
		void
			generatePipelineTest();
		void
			generatePipelineVisualization( void );

		std::string name_;
		EquationMap equations_;
		CodeMap codes_;
		BufferMap buffers_;
		Image2DMap image2Ds_;
		Image3DMap image3Ds_;
		UniformMap uniforms_;
		KernelMap kernels_;
		StageMap stages_;
		GraphMap graphs_;
		ASTVarDefMap environment_;
	};


	// A PipelineContainer class holds a set of pipelines

	class PipelineContainer
	{
	public:
		PipelineContainer(){}
		PipelineContainer( std::string name )
		{
			name_ = name;
		}
		void 
			declarePipeline( std::string name, Pipeline * pipeline )
		{
			pipelines()[ name ] = pipeline;
		}
		std::string
			name()
		{
			return name_;
		}
		PipelineMap & pipelines()
		{
			return pipelines_;
		}
		void
			generatePipelines()
		{
			PipelineMap::iterator pmIt;
			pmIt = pipelines().begin();
			while( pmIt != pipelines().end() ){
				Pipeline * p = pmIt->second;
				p->generateRuntimePipeline();
				pmIt++;
			}//while
		}
	private:
		std::string name_;
		PipelineMap pipelines_;
	};


	// Clase AttributeContainer

	class AttributeContainer : public AttributeContainerBase{
	public:
		AttributeContainer(){}
		ASTVarDefMap
			environment()
		{
			return pipeline()->environment();
		}
		Maybe<ASTExpr>::MaybeType 
			maybeExprFromValue( ASTExpr value )
		{
			return Maybe<ASTExpr>::MaybeType( value );
		}
		Maybe<ASTExpr>::MaybeType 
			maybeExpr( std::string name );
		bool 
			isDefined( std::string name );
		int
			intValue( std::string name )
		{
			Maybe<ASTExpr>::MaybeType me = maybeExpr( name );
			if( Maybe<ASTExpr>::toBool( me )){
				ASTExpr e = Maybe<ASTExpr>::toJust( me );
				return( evaluate<int>( pipeline()->environment(), e ));
			}
			assert( 0 );
			return 0;
		}
		float
			floatValue( std::string name )
		{
			Maybe<ASTExpr>::MaybeType me = maybeExpr( name );
			if( Maybe<ASTExpr>::toBool( me )){
				ASTExpr e = Maybe<ASTExpr>::toJust( me );
				return( evaluate<float>( pipeline()->environment(), e ));
			}
			assert( 0 );
			return 0;
		}
		std::string
			stringValue( std::string name )
		{
			Maybe<ASTExpr>::MaybeType me = maybeExpr( name );
			if( Maybe<ASTExpr>::toBool( me )){
				ASTExpr e = Maybe<ASTExpr>::toJust( me );
				return stringify( e );
			}
			assert( 0 );
			return NULL;
		}

	private:
	};



	// The Code class represents verbatim code to inject into the generated class.

	class Code : public AttributeContainer{
	public:
		Code( 
			CODE_SECTION codeType, 
			std::string code, 
			ASTAttributeList & attributes, 
			Pipeline * pipeline )
		{
			codeType_ = codeType;
			code_ = code;
			pipelineIs( pipeline );
			importAttributes( attributes );
		}
		virtual ~Code(){}
		CODE_SECTION 
			codeType()
		{
			return codeType_;
		}
		std::string 
			code()
		{
			return code_;
		}
		std::string 
			name()
		{
			if( codeType_ == AST_CODECPP ){
				return "C";
			} else if( codeType_ == AST_CODECL ){
				return "CL";
			}
			return NULL;
		}
	private:
		CODE_SECTION codeType_;
		std::string code_;
	};



	// The Buffer class represents a buffer or image.

	class Buffer : public AttributeContainer, public Node{
	public:
		Buffer(){}
		Buffer( 
			std::string name,
			ADDRESS_SPACE addressSpace, 
			std::string type, 
			ASTExpr size, 
			ASTAttributeList attributes,
			Pipeline * pipeline );
		virtual ~Buffer(){}

		void 
			set( 
			std::string name,
			ADDRESS_SPACE addressSpace, 
			std::string type, 
			ASTExpr size, 
			ASTAttributeList attributes,
			Pipeline * pipeline );
		virtual std::string
			typeName()
		{
			return "Buffer";
		}
		std::string 
			name()
		{
			return name_;
		}
		std::string
			UAVname()
		{
			return "UAV" + name();
		}
		std::string
			SRVname()
		{
			return "SRV" + name();
		}
		ADDRESS_SPACE 
			addressSpace()
		{
			return addressSpace_;
		}
		std::string 
			type()
		{
#ifdef __APPLE__
			// bug in opencl compiler? remove if this gets fixed
			if( !type_.compare( "uint" ) ){
				return "unsigned int";
			}
			if( !type_.compare( "ulong" ) ){
				return "unsigned long";
			}
#endif
			return type_;
		}
		ASTExpr 
			size()
		{
			return size_;
		}
		virtual ASTExpr sizeX() { return size(); }
		virtual ASTExpr sizeY() { return size(); }
		virtual ASTExpr sizeZ() { return size(); }
		std::string
			sizeString()
		{
			ASTExpr s = size();
			std::string result = stringify( s );
			return result;
		}
		virtual std::string
			dimensionsSizeT()
		{
			std::string result;
			ASTExpr v = size();
			std::string s = stringify( v );
			std::stringstream stringStream;
			stringStream << baseCount();
			std::string baseCountString;
			stringStream >> baseCountString;

			result = "( ( " + s + " ) * sizeof( " + baseType() + " ) * " + 
				baseCountString + " )";
			return result;
		}
		virtual std::string
			dimensions()
		{
			std::string result;
			ASTExpr v = size();
			result = stringify( v );
			return result;
		}
		virtual std::string
			totalSize()
		{
			std::string result;
			ASTExpr v = size();
			result = stringify( v );
			return result;
		}
		virtual std::string
			totalSizeT()
		{
			return dimensionsSizeT();
		}
		std::string
			byteWidthString()
		{
			std::string baseCountString = boost::lexical_cast<string>( baseCount() );
			std::string result = "( (" + sizeString() + ") * " + baseCountString + " * sizeof( " + baseType() + " ) )";
			return result;
		}
		virtual std::string
			kernelSignature( Kernel * k )
		{
			std::string result;
			if( addressSpace() == AST_GLOBAL ){
				result = "__global " + type() + " * " + name();
			}
			else{
				result = "__local " + type() + " * " + name();
			}
			return result;
		}
		virtual std::string
			imageFormat()
		{
			return "";
		}
		virtual bool
			isImage()
		{
			return false;
		}
		virtual unsigned int
			dimensionality()
		{
			return 1;
		}
		bool 
			isInput()
		{
			return isInput_;
		}
		void
			isInputIs( bool value )
		{
			isInput_ = value;
		}
		bool
			isOutput()
		{
			return isOutput_;
		}
		void
			isOutputIs( bool value )
		{
			isOutput_ = value;
		}
		bool
			isUsedAsOutput( Stage * s );
		bool
			isUsedAsOutput( Kernel * k );
		std::string
			baseType()
		{
			typedef struct
			{
				std::string type;
				std::string baseType;
			} structType;
			structType s[] = {
				{ "int", "int" },
				{ "int2", "int" },
				{ "int3", "int" },
				{ "int4", "int" },
				{ "uint", "unsigned int" },
#ifdef __APPLE__
				{ "unsigned int", "unsigned int" },
#endif
				{ "uint2", "unsigned int" },
				{ "uint3", "unsigned int" },
				{ "uint4", "unsigned int" },
				{ "float", "float" },
				{ "float2", "float" },
				{ "float3", "float" },
				{ "float4", "float" }
			};
			int sLen = sizeof( s ) / sizeof( structType );
			for( int i = 0; i < sLen; ++i ){
				if( 0 == s[ i ].type.compare( type() ) ){
					return s[ i ].baseType;
				}
			}//for
			assert( 0 );
			return NULL;
		}
		int
			baseCount()
		{
			typedef struct
			{
				std::string type;
				int baseCount;
			} structType;
			structType s[] = {
				{ "int", 1 },
				{ "int2", 2 },
				{ "int3", 3 },
				{ "int4", 4 },
				{ "uint", 1 },
#ifdef __APPLE__
				{ "unsigned int", 1 },
#endif
				{ "uint2", 2 },
				{ "uint3", 3 },
				{ "uint4", 4 },
				{ "float", 1 },
				{ "float2", 2 },
				{ "float3", 3 },
				{ "float4", 4 }
			};
			int sLen = sizeof( s ) / sizeof( structType );
			for( int i = 0; i < sLen; ++i ){
				if( 0 == s[ i ].type.compare( type() ) ){
					return s[ i ].baseCount;
				}
			}//for
			throw std::runtime_error( "unrecognized data type " + type() );
			return NULL;
		}
		bool 
		isVectorType()
		{
			return baseCount() > 1;
		}
		std::string
			captureFunction()
		{
			typedef struct
			{
				std::string type;
				std::string captureFunction;
			} structType;
			structType s[] = {
				{ "int", "captureTextDataInt" },
				{ "int2", "captureTextDataInt2" },
				{ "int3", "captureTextDataInt3" },
				{ "int4", "captureTextDataInt4" },
				{ "uint", "captureTextDataUint" },
#ifdef __APPLE__
				{ "unsigned int", "captureTextDataUint" },
#endif
				{ "uint2", "captureTextDataUint2" },
				{ "uint3", "captureTextDataUint3" },
				{ "uint4", "captureTextDataUint4" },
				{ "float", "captureTextDataFloat" },
				{ "float2", "captureTextDataFloat2" },
				{ "float3", "captureTextDataFloat3" },
				{ "float4", "captureTextDataFloat4" }
			};
			int sLen = sizeof( s ) / sizeof( structType );
			for( int i = 0; i < sLen; ++i ){
				if( 0 == s[ i ].type.compare( type() ) ){
					return s[ i ].captureFunction;
				}
			}//for
			assert( 0 );
			return NULL;
		}
		void
			selfDeclareType( ofstream * classFile, std::string type );
		void
			selfDeclare( ofstream * classFile );
		void
			selfInitialize( ofstream * classFile );
		void
			selfTerminate( ofstream * classFile );
		void
			selfCaptureData( ofstream * classFile, Stage * stage );
		void
			selfLoadFromDirectComputeDevice( ofstream * classFile );
		void
			selfLoadFromOpenCLDevice( ofstream * classFile );
		void
			selfLoadFromDevice( ofstream * classFile );
		void
			selfDeclareDirectComputeBuffer( 
			ofstream * kernelFile, 
			Kernel * k, 
			int & idUAV,
			int & idSRV
			);
		void
			setDirectComputeAccessView( 
			ofstream * classFile, 
			std::string lastArg, 
			Stage * s,
			unsigned int & srvNumber,
			unsigned int & uavNumber
			);
		void
			check();

	public:
		std::string name_;
		ADDRESS_SPACE addressSpace_;
		std::string type_;
		ASTExpr size_;
		bool isInput_;
		bool isOutput_;
	};


	class Image : public Buffer{
	public:
		Image(){}
	private:
	};


	class Image2D : public Image{
	public:
		Image2D(){}
		Image2D( std::string name,
			ADDRESS_SPACE addressSpace, 
			std::string type, 
			ASTExpr sizeX, 
			ASTExpr sizeY,
			ASTAttributeList attributes,
			Pipeline * pipeline )
		{
			pipelineIs( pipeline );
			name_ = name;
			addressSpace_ = addressSpace;
			type_ = type;
			sizeX_ = sizeX;
			int sizex = evaluate< int >( pipeline->environment(), sizeX );
			sizeY_ = sizeY;
			int sizey = evaluate< int >( pipeline->environment(), sizeY );
			importAttributes( attributes );
		}
		virtual ~Image2D(){}
		std::string
			typeName()
		{
			return "Image2D";
		}
		ASTExpr
			sizeX()
		{
			return sizeX_;
		}

		ASTExpr
			sizeY()
		{
			return sizeY_;
		}
		std::string
			dimensionsSizeT()
		{
			std::string result;

			ASTExpr vx = sizeX();
			std::string sx = stringify( vx );
			ASTExpr vy = sizeY();
			std::string sy = stringify( vy );

			std::stringstream stringStream;
			stringStream << baseCount();
			std::string baseCountString;
			stringStream >> baseCountString;

			result = "( ( " + sx + " ) * sizeof( " + baseType() + " ) * " + 
				baseCountString + " ), " +
				"( ( " + sy + " ) * sizeof( " + baseType() + " ) * " + 
				baseCountString + " )";

			return result;
		}
		std::string
			dimensions()
		{
			std::string result;

			ASTExpr vx = sizeX();
			std::string sx = stringify( vx );
			ASTExpr vy = sizeY();
			std::string sy = stringify( vy );

			result = "( " + sx + " ), ( " + sy + " )";
			return result;
		}
		std::string
			totalSize()
		{
			std::string result;

			ASTExpr vx = sizeX();
			std::string sx = stringify( vx );
			ASTExpr vy = sizeY();
			std::string sy = stringify( vy );

			result = "( ( " + sx + " ) * ( " + sy + " ) )";
			return result;
		}
		std::string
			totalSizeT()
		{
			std::string result;

			ASTExpr vx = sizeX();
			std::string sx = stringify( vx );
			ASTExpr vy = sizeY();
			std::string sy = stringify( vy );

			std::stringstream stringStream;
			stringStream << baseCount();
			std::string baseCountString;
			stringStream >> baseCountString;

			result = "( ( " + sx + " ) * ( " + sy + " ) * " +
				"sizeof( " + baseType() + " ) * " + baseCountString + " )";

			return result;
		}
		unsigned int
			dimensionality()
		{
			return 2;
		}
		std::string 
			kernelSignature( Kernel * k )
		{
			std::string result;
			if( !isUsedAsOutput(k ) ){
				result = "__read_only image2d_t " + name();
			}
			else{
				result = "__write_only image2d_t " + name();
			}
			return result;
		}

		std::string
			imageFormat()
		{
			std::string result;
			result = "cl::ImageFormat cif;";
			switch( baseCount() ){
				case 1: result += "cif.image_channel_order=CL_R;"; break;
				case 2: result += "cif.image_channel_order=CL_RG;"; break;
				case 3: assert( "cannot have three element data in images" ); break;
				case 4: result += "cif.image_channel_order=CL_RGBA;"; break;
			}//switch
			if( 0 == baseType().compare( "int" ) ){
				result += "cif.image_channel_data_type=CL_SIGNED_INT32;"; 
			}
			else if( 0 == baseType().compare( "unsigned int" ) ){
				result += "cif.image_channel_data_type=CL_UNSIGNED_INT32;"; 
			}
			else if( 0 == baseType().compare( "float" ) ){
				result += "cif.image_channel_data_type=CL_FLOAT;";
			}
			return result;
		}
		bool
			isImage()
		{
			return true;
			}


	public:
		ASTExpr sizeX_;
		ASTExpr sizeY_;
	};


	class Image3D : public Image2D{
	public:
		Image3D(){}
		Image3D( std::string name,
			ADDRESS_SPACE addressSpace, 
			std::string type, 
			ASTExpr sizeX, 
			ASTExpr sizeY,
			ASTExpr sizeZ,
			ASTAttributeList attributes,
			Pipeline * pipeline )
		{
			pipelineIs( pipeline );
			name_ = name;
			addressSpace_ = addressSpace;
			type_ = type;
			int sizex = evaluate< int >( pipeline->environment(), sizeX );
			sizeX_ = sizeX;
			int sizey = evaluate< int >( pipeline->environment(), sizeY );
			sizeY_ = sizeY;
			int sizez = evaluate< int >( pipeline->environment(), sizeZ );
			sizeZ_ = sizeZ;
			importAttributes( attributes );
		}
		virtual ~Image3D(){}
		std::string
			typeName()
		{
			return "Image3D";
		}
		ASTExpr
			sizeZ()
		{
			return sizeZ_;
		}
		std::string
			dimensionsSizeT()
		{
			std::string result;

			ASTExpr vx = sizeX();
			std::string sx = stringify( vx );
			ASTExpr vy = sizeY();
			std::string sy = stringify( vy );
			ASTExpr vz = sizeZ();
			std::string sz = stringify( vz );

			std::stringstream stringStream;
			stringStream << baseCount();
			std::string baseCountString;
			stringStream >> baseCountString;

			result = "( ( " + sx + " ) * sizeof( " + baseType() + " ) * " + 
				baseCountString + " ), " +
				"( ( " + sy + " ) * sizeof( " + baseType() + " ) * " + 
				baseCountString + " ), " +
				"( ( " + sz + " ) * sizeof( " + baseType() + " ) * " + 
				baseCountString + " )";

			return result;
		}
		std::string
			dimensions()
		{
			std::string result;

			ASTExpr vx = sizeX();
			std::string sx = stringify( vx );
			ASTExpr vy = sizeY();
			std::string sy = stringify( vy );
			ASTExpr vz = sizeZ();
			std::string sz = stringify( vz );

			result = "( " + sx + " ), ( " + sy + " ), ( " + sz + " )";
			return result;
		}
		std::string
			totalSize()
		{
			std::string result;

			ASTExpr vx = sizeX();
			std::string sx = stringify( vx );
			ASTExpr vy = sizeY();
			std::string sy = stringify( vy );
			ASTExpr vz = sizeZ();
			std::string sz = stringify( vz );

			result = "( ( " + sx + " ) * ( " + sy + " ) * ( " + sz + " ) )";
			return result;
		}
		std::string
			totalSizeT()
		{
			std::string result;

			ASTExpr vx = sizeX();
			std::string sx = stringify( vx );
			ASTExpr vy = sizeY();
			std::string sy = stringify( vy );
			ASTExpr vz = sizeZ();
			std::string sz = stringify( vz );

			std::stringstream stringStream;
			stringStream << baseCount();
			std::string baseCountString;
			stringStream >> baseCountString;

			result = "( ( " + sx + " ) * ( " + sy + " ) * ( " + sz + " ) * " +
				"sizeof( " + baseType() + " ) * " + baseCountString + " )";

			return result;
		}
		unsigned int
			dimensionality()
		{
			return 3;
		}
		std::string 
			kernelSignature( Kernel * k )
		{
			std::string result;
			if( !isUsedAsOutput(k ) ){
				result = "__read_only image3d_t " + name();
			}
			else{
				result = "__write_only image3d_t " + name();
			}
			return result;
		}

	private:
		ASTExpr sizeZ_;
	};


	// The Uniform class represents a scalar parameter to a kernel.

	class Uniform : public AttributeContainer, public Node {
	public:
		Uniform(){}
		Uniform( std::string name, 
			std::string type, 
			ASTAttributeList attributes,
			Pipeline * pipeline )
		{
			name_ = name;
			type_ = type;
			importAttributes( attributes );
			pipelineIs( pipeline );
		}
		virtual ~Uniform(){}

		std::string 
			name()
		{
			return name_;
		}
		std::string 
			type()
		{
			return type_;
		}
		unsigned int
			size()
		{
			if( !type().compare( "int" ) ){
				return sizeof( int );
			}
			else if( !type().compare( "float" ) ){
				return sizeof( float );
			}
			return 0;
		}
		void
			selfDeclare( ofstream * classFile );
		void
			selfUpdate( ofstream * classFile )
		{
			if( isDefined( "_" )){
				ASTExpr rhs = expression( "_" );
				*classFile << name() << " = " << stringify( rhs ) << ";" << std::endl;
			}
		}
		void
			selfUpdateStruct( ofstream * classFile, Stage * s );
		void
			selfGenerateUniformBuffers( ofstream * classFile );
		void
			selfDeclareAccessorMutator( ofstream * classFile );
		void
			generateDependantEvaluations( ofstream * classFile );
		void
			check();

	private:
		std::string name_;
		std::string type_;
	};

	// The Equation class represents an equation of the form name = expression

	class Equation : public AttributeContainer, public Node{
	public:
		Equation( std::string name, std::string type, ASTExpr value, Pipeline * pipeline )
		{
			name_ = name;
			type_ = type;
			value_ = value;
			pipeline_ = pipeline;
			evaluated_ = false;
			undefined_ = false;
		}
		std::string 
			name()
		{
			return name_;
		}
		std::string 
			type()
		{
#if 0
			return type_;
#else
			UniformMap::iterator unIt = pipeline()->uniforms().find( name() );
			if( unIt != pipeline()->uniforms().end() ){
				Uniform * uniform = unIt->second;
				return uniform->type();
			}
			return type_;//obsolete
#endif
		}
		void typeIs( std::string type ){
#if 0
			type_ = type;
#else
			assert( false );//don't try to set type of equation
#endif
		}
		ASTExpr 
			value()
		{
			return value_;
		}
		Pipeline * 
			pipeline()
		{
			return pipeline_;
		}
		bool
			evaluated()
		{
			return evaluated_;
		}
		void
			evaluatedIs( bool evaluated )
		{
			evaluated_ = evaluated;
		}
		bool
			undefined()
		{
			bool result = undefined_;
			ASTExpr v = value();
			result |= !stringify( v ).compare( "undefined" );
			return result;
		}
		void
			undefinedIs( bool undefined )
		{
			undefined_ = undefined;
		}
		void
			selfDeclare( ofstream * classFile );
		void
			selfDeclareAccessorMutator( ofstream * classFile );
		void
			generateDependantEvaluations( ofstream * classFile );
		void
			generateSelfEvaluationAssignment( ofstream * classFile );
		void
			generateSelfEvaluation( ofstream * classFile );
		void
			selfInvokeEvaluation( ofstream * classFile );
		void
			generatePredecessorEvaluations( ofstream * classFile );
		bool
			allPrecedentsEvaluated();
		void
			check();

	private:
		std::string name_;
		std::string type_;
		ASTExpr value_;
		Pipeline * pipeline_;
		bool evaluated_;
		bool undefined_;
	};


	// The StageBaseClass holds functionality that is common to Stage and Kernel classes.

	class StageBaseClass : public AttributeContainer{
	public:
#if 0//old
		BufferMap &
			inputs()
		{
			return inputs_;
		}
		UniformMap &
			uniforms()
		{
			return uniforms_;
		}
		BufferMap &
			outputs()
		{
			return outputs_;
		}
		BufferVector &
			inputsVector()
		{
			return inputsVector_;
		}
		UniformVector &
			uniformsVector()
		{
			return uniformsVector_;
		}
		BufferVector &
			outputsVector()
		{
			return outputsVector_;
		}
#else
		NodeList & arguments(){ return arguments_; }
#endif

#if 0
		void
			importMapsFromNameList( 
			NameList nameList, 
			BufferMap &buffers, 
			UniformMap &uniforms,
			BufferVector &buffersVector,
			UniformVector &uniformsVector,
			bool isInput );
#endif

		void
			importArgumentsFromNameList( 
			NameList nameList,
			NodeList & arguments );

#if 0
		void
			importMapsFromASTTypeVarList( 
			ASTTypeVarList parameterList,
			BufferMap & buffers,
			UniformMap & uniforms,
			BufferVector &buffersVector,
			UniformVector &uniformsVector,
			bool isInput );
#endif
		void
			importParametersFromASTTypeVarList( 
			ASTTypeVarList parameterList,
			KernelParameterList & parameters );

	private:
#if 0
		BufferMap inputs_;
		UniformMap uniforms_;
		BufferMap outputs_;
		BufferVector inputsVector_;
		UniformVector uniformsVector_;
		BufferVector outputsVector_;
#endif
		NodeList arguments_;
	};

	// The Kernel class represents a kernel, which may be used in multiple stages.

	class Kernel : public StageBaseClass, public Node{
	public:
		Kernel(){}
		Kernel( 
		std::string name,
#if 1
		ASTTypeVarList parameters,
#else
		ASTTypeVarList inputParameters, 
		ASTTypeVarList outputParameters, 
#endif
		ASTAttributeList attributes ,
		Pipeline * pipeline )		
		{
			pipelineIs( pipeline );
			name_ = name;
#if 1
			importParametersFromASTTypeVarList( parameters, kernelParameters() );

#else
			importMapsFromASTTypeVarList( inputParameters, inputs(), uniforms(), inputsVector(), uniformsVector(), true );
			importMapsFromASTTypeVarList( outputParameters, outputs(), uniforms(), outputsVector(), uniformsVector(), false );
#endif
			importAttributes( attributes );
#if 0
			inputParametersIs( inputParameters );
#endif
		}

		std::string
			name()
		{
			return name_;
		}
#if 0
		ASTTypeVarList &
			inputParameters()
		{
			return inputParameters_;
		}
		void
			inputParametersIs( ASTTypeVarList inputParameters )
		{
			inputParameters_ = inputParameters;
		}
#endif	
		KernelParameterList & kernelParameters(){ return kernelParameters_; }
		void
			selfGenerateOpenCLHeader( ofstream * kernelFile );
		void
			selfGenerateUniformBuffers( ofstream * classFile );
		void
			selfGenerateDirectComputeHeader( ofstream * kernelFile );
		void
			selfGenerateHeader( ofstream * kernelFile );
		void
			selfDeclare( ofstream * classFile );
		void
			selfInitialize( ofstream * classFile );
		void
			check();

	private:
		std::string name_;
#if 0
		ASTTypeVarList inputParameters_;
		ASTTypeVarList outputParameters_;
#endif
		KernelParameterList kernelParameters_;
	};


	// The Stage class represents a unique invocation of a kernel, or an externally supplied host function.

	class Stage : public StageBaseClass, public Node{
	public:
		Stage(){}
		Stage( std::string name,
			std::string kernelName,
#if 1
			NameList args,
#else
			NameList inputs, 
			NameList outputs, 
#endif
			ASTAttributeList attributes,
			Pipeline * pipeline );
		virtual ~Stage(){}

		std::string 
			name()
		{
			return name_;
		}
		Kernel * 
			kernel()
		{
			return kernel_;
		}
		bool
			isDeclaredExternally()
		{
			return isTrue( "external" ) || isTrue( "branchSelector" );
		}
		bool
			isBarrierStage()
		{
			return isDefined( "barrier" );
		}
		bool
			isSynchronizationStage()
		{
			return isBarrierStage();
		}
		void
			inputNamesIs( NameList inputNames )
		{
			inputNames_ = inputNames;
		}
		NameList &
			inputNames()
		{
			return inputNames_;
		}
		void
			graphVertexIs( GraphVertex * graphVertex )
		{
			graphVertex_ = graphVertex;
		}
		GraphVertex *
			graphVertex()
		{
			return graphVertex_;
		}

		std::string
			kernelUniform( Uniform * stageUniform )
		{
			// this method returns the name of the kernel parameter that corresponds
			//  to the stage argument stageUniform
			KernelParameterList::iterator kplIt = kernel()->kernelParameters().begin();
			for( NodeList::iterator nIt = arguments().begin(); 
				nIt != arguments().end(); ++nIt, ++kplIt ){
					if( *nIt == stageUniform ){
						return (*kplIt).name;
					}
			}//for
#if 0
			ASTTypeVarList kernelUniforms = kernel()->inputParameters();
			NameList::iterator namIt;
			namIt = inputNames().begin();
			int position = 0;
			while( namIt != inputNames().end() ){
				std::string argumentName = *namIt;
				if( 0 == argumentName.compare( stageUniform->name() ) ){
					ASTTypeVarList::iterator atvIt;
					atvIt = kernelUniforms.begin();
					ASTTypeVarElement tuple;
					for( int i = 0; i <= position; ++i ){
						tuple = *atvIt;
						atvIt++;
					}//for
					std::string parameterName = boost::get<2>( tuple );
					return parameterName;
				}
				namIt++;
				position++;
			}//while
			return NULL;
#endif
		}

		void
			selfDeclareExternal( ofstream * classFile );
		void
			selfInvokeExternal( ofstream * classFile );
		void
			selfImplementExternal( ofstream * extFile );
		void
			selfDeclareTest( ofstream * classFile );
		void
			selfImplementTest( ofstream * testFile );
		void
			selfGenerateSetArgs( ofstream * classFile );
		void
			selfCaptureInputs( ofstream * classFile );
		void
			selfCaptureOutputs( ofstream * classFile );
		void
			selfInitializeUniformBuffers( ofstream * classFile );
		void
			selfUpdateUniforms( ofstream * classFile );
		void
			selfInvokeComputeShader( ofstream * classFile );
		std::string
			globalNDRange();
		std::string
			localNDRange();
		void
			selfInvokeRun( ofstream * classFile );
		void 
			selfInvokeTest( ofstream * classFile );
		void
			selfInvokeCapture( ofstream * classFile );
		void
			selfTerminate( ofstream * classFile );
		void
			selfDeclareDirectComputeBuffers( ofstream * classFile );
		void
			selfGenerateUniformBuffers( ofstream * classFile );
		void
			declareSynchronizationObject( ofstream * classFile );
		void
			check();

	private:
		std::string name_;
		Kernel * kernel_;
		NameList inputNames_;
		GraphVertex * graphVertex_;
	};


	// The Graph class describes the topology of the computational pipeline.  

	class Graph;

	class GraphVertex{
	public:
		GraphVertex(){}
		GraphVertex( Stage * stage, Graph * graph ){
			stage_ = stage;
			graph_ = graph;
			basicBlocksGenerated_ = false;
		}
		virtual ~GraphVertex(){}

		Stage * 
			stage()
		{
			return stage_;
		}
		GraphVertexChildren &
			children()
		{
			return children_;
		}
		Graph *
			graph()
		{ 
			return graph_;
		}
		std::string
			CGVName()
		{
			return string( "_CGV" ) + "graphName" /*graph()->name()*/ + stage()->name();
		}
		bool
			isBranchSelector()
		{
			return( children().size() > 1 || stage()->isTrue( "branchSelector" ));
		}
		bool
			basicBlocksGenerated()
		{
			return basicBlocksGenerated_;
		}
		void
			basicBlocksGeneratedIs( bool basicBlocksGenerated )
		{
			basicBlocksGenerated_ = basicBlocksGenerated;
		}
		void
			selfDeclare( ofstream * classFile );
		void
			selfAssign( ofstream * classFile );
		void
			generateVisualization( ofstream * dotFile );
	private:
		Stage * stage_;
		GraphVertexChildren children_;
		Graph * graph_;
		bool basicBlocksGenerated_;
	};


	class Graph : public AttributeContainer{
	public:
		Graph(){}
		Graph( std::string name,
			ASTGraphEdgeList edges, 
			ASTAttributeList attributes,
			Pipeline * pipeline );
		virtual ~Graph(){}
		std::string 
			name()
		{
			return name_;
		}
		GraphVertexMap &
			vertices()
		{
			return vertices_;
		}
		void
			clearState()
		{
			GraphVertexMap::iterator gvIt;
			gvIt = vertices().begin();
			while( gvIt != vertices().end() ){
				GraphVertex * GV = gvIt->second;
				GV->basicBlocksGeneratedIs( false );
				gvIt++;
			}//while
		}
		void 
			importEdgeList( ASTGraphEdgeList edges );
		void
			invokeStage( ofstream * classFile );
		void
			generateVisualization( ofstream * dotFile );
		void
			generateBasicBlocks( ofstream * classFile, GraphVertex * graphVertex, std::string prefix );
		void
			generateExecute( ofstream * classFile, std::string prefix );
		void
			check();

	private:
		std::string name_;
		GraphVertexMap vertices_;
		ASTGraphEdgeList edges_;
	};




}//namespace amd

#endif

